/**
 * @api post controller.
 *
 * User: yunshengzhu
 * Date: 2020-02-01
 * Time: 12:13
 */
package control

import (
	"bufio"
	"bytes"
	"card-platform/config"
	"crypto/aes"
	cp "crypto/cipher"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"pkg/cfg"
	"regexp"
	"strings"
	"time"
)

var accessToken string
var accessTokenTime int64

type AccessModel struct {
	AccessToken string `json:"access_token"`
	ExpiresIn   int64  `json:"expires_in"`
}

func DownImg(url string) string {
	fileName := path.Base(url) + ".png"
	res, err := http.Get(url)
	if err != nil {
		cfg.LogErr("err:", err)
		return ""
	}
	defer res.Body.Close()
	file, err := os.Create(getCurrentDirectory() + "/upload/headimg/" + fileName)
	if err != nil {
		cfg.LogErr("err:", err)
		return "/upload/headimg/" + fileName
	}
	// 获得文件的writer对象
	writer := bufio.NewWriter(file)
	body, _ := ioutil.ReadAll(res.Body)
	written, _ := io.Copy(writer, bytes.NewReader(body))
	fmt.Printf("Total length: %d", written)
	return "/upload/headimg/" + fileName
}

func WxACode(code string, userId int64) (string, error) {
	accessToken, err := GetAccessToken()
	if err != nil {
		cfg.LogErr("err:", err)
		return "", err
	}
	param := make(map[string]string)
	param["path"] = fmt.Sprintf("pages/home/index?code=%s", code)
	param["width"] = "350"
	data, err := json.Marshal(param)
	url := fmt.Sprintf(`https://api.weixin.qq.com/wxa/getwxacode?access_token=%s`, accessToken)
	resp, err := http.Post(url, "application/xml; charset=utf-8", bytes.NewBuffer(data))
	if err != nil {
		return "", err
	}
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		cfg.LogErr("err:", err)
		return "", err
	}
	pathImg := fmt.Sprintf(`/upload/code/%d.png`, userId)
	file, err := os.OpenFile(getCurrentDirectory()+pathImg, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		cfg.LogErr("err:", err)
		return "", err
	}
	w := bufio.NewWriter(file) //创建新的 Writer 对象
	_, err3 := w.Write(b)
	if err3 != nil {
		return "", err
	}
	w.Flush()
	defer file.Close()
	return pathImg, nil
}

func WxProCode(code string, proId, userId int64) (string, error) {
	accessToken, err := GetAccessToken()
	if err != nil {
		cfg.LogErr("err:", err)
		return "", err
	}

	data := []byte(fmt.Sprintf(`{"path": "pages/commodityDetails/index?id=%d&code=%s","width": "350"}`, proId, code))
	cfg.LogInfo("data:", string(data))

	url := fmt.Sprintf(`https://api.weixin.qq.com/wxa/getwxacode?access_token=%s`, accessToken)
	resp, err := http.Post(url, "application/xml; charset=utf-8", bytes.NewBuffer(data))
	if err != nil {
		return "", err
	}
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		cfg.LogErr("err:", err)
		return "", err
	}
	pathImg := fmt.Sprintf(`/upload/code/%d_%d.png`, proId, userId)
	file, err := os.OpenFile(getCurrentDirectory()+pathImg, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		cfg.LogErr("err:", err)
		return "", err
	}
	w := bufio.NewWriter(file) //创建新的 Writer 对象
	_, err3 := w.Write(b)
	if err3 != nil {
		return "", err
	}
	w.Flush()
	defer file.Close()
	return pathImg, nil
}

type WXUser struct {
	Name       string
	CreateTime int64
	Openid     string
	City       string
	Country    string
	Province   string
	HeadImgUrl string
	Sex        string
}

type WXInfo struct {
	Openid     string      `json:"openid"`
	Nickname   interface{} `json:"nickname"`
	City       interface{} `json:"city"`
	Country    interface{} `json:"country"`
	Province   interface{} `json:"province"`
	HeadimgUrl interface{} `json:"headimgurl"`
	Sex        int         `json:"sex"`
}

func GetWeixinInfo(AccessToken string, Openid string) (*WXUser, error) {
	//获取用户信息
	userInfoUrl := strings.Join([]string{"https://api.weixin.qq.com/sns/userinfo",
		"?access_token=", AccessToken,
		"&openid=", Openid,
		"&lang=zh_CN"}, "")
	infoBody, err := http.Get(userInfoUrl)
	defer infoBody.Body.Close()
	info, _ := ioutil.ReadAll(infoBody.Body)
	updateUser := WXInfo{}

	cfg.LogInfo("=======info:", string(info))
	json.Unmarshal(info, &updateUser)
	user := WXUser{Openid: updateUser.Openid}
	if err != nil {
		return &user, err
	} else {
		user.City = updateUser.City.(string)
		user.Name = updateUser.Nickname.(string)
		user.Country = updateUser.Country.(string)
		user.Province = updateUser.Province.(string)
		user.HeadImgUrl = updateUser.HeadimgUrl.(string)

		if updateUser.Sex == 1 {
			user.Sex = "男"
		} else if updateUser.Sex == 2 {
			user.Sex = "女"
		} else {
			user.Sex = "保密"
		}

		return &user, err
	}
}

func GetAccessToken() (string, error) {
	if accessTokenTime > time.Now().Unix() {
		return accessToken, nil
	}
	url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", config.APPID, config.APPSECRET)
	resp, err := http.Get(url)
	//关闭资源
	if resp != nil && resp.Body != nil {
		defer resp.Body.Close()
	}
	if err != nil {
		return "", errors.New("request token err :" + err.Error())
	}
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		cfg.LogErr("err:", err)
		return "", err
	}
	cfg.LogInfo("b:", string(b))

	acc := AccessModel{}

	err = json.Unmarshal(b, &acc)
	if err != nil {
		cfg.LogErr("err:", err)
	}

	if len(acc.AccessToken) <= 0 {
		return "", errors.New("获取失败")
	}

	accessToken = acc.AccessToken
	accessTokenTime = acc.ExpiresIn + time.Now().Unix() - 120
	return accessToken, nil
}

func GetWxUserInfo(accessToken, openId string) (string, error) {
	url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s&lang=zh_CN", accessToken, openId)
	resp, err := http.Get(url)
	//关闭资源
	if resp != nil && resp.Body != nil {
		defer resp.Body.Close()
	}
	if err != nil {
		return "", errors.New("request token err :" + err.Error())
	}

	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		cfg.LogErr("err:", err)
		return "", err
	}

	cfg.LogInfo("b:", string(b))

	return string(b), nil
}

func WechatLogin(jsCode, appid, secret string) (map[string]interface{}, error) {
	//Code2SessURL := "https://api.weixin.qq.com/sns/jscode2session?appid={appid}&secret={secret}&code={code}&grant_type=authorization_code"
	Code2SessURL := "https://api.weixin.qq.com/sns/oauth2/access_token?appid={appid}&secret={secret}&code={code}&grant_type=authorization_code"
	Code2SessURL = strings.Replace(Code2SessURL, "{appid}", appid, -1)
	Code2SessURL = strings.Replace(Code2SessURL, "{secret}", secret, -1)
	Code2SessURL = strings.Replace(Code2SessURL, "{code}", jsCode, -1)
	resp, err := http.Get(Code2SessURL)
	//关闭资源
	if resp != nil && resp.Body != nil {
		defer resp.Body.Close()
	}
	if err != nil {
		return nil, errors.New("WechatLogin request err :" + err.Error())
	}

	var jMap map[string]interface{}
	err = json.NewDecoder(resp.Body).Decode(&jMap)
	if err != nil {
		return nil, errors.New("request token response json parse err :" + err.Error())

	}
	if _, ok := jMap["errcode"]; !ok || jMap["errcode"] == 0 {
		return jMap, nil
	} else {
		cfg.LogInfo("jMap:", jMap)
		//返回错误信息
		errcode := fmt.Sprint(jMap["errcode"])
		errmsg := fmt.Sprint(jMap["errmsg"])
		err = errors.New(errcode + ":" + errmsg)
		return nil, err
	}
}

type UserInfoSubscribe struct {
	Subscribe int    `json:"subscribe"`
	Openid    string `json:"openid"`
	Nickname  string `json:"nickname"`
}

func UserInfo(accountToken, openid string) (out UserInfoSubscribe, err error) {
	Code2SessURL := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s&lang=zh_CN", accountToken, openid)
	resp, err := http.Get(Code2SessURL)
	//关闭资源
	if resp != nil && resp.Body != nil {
		defer resp.Body.Close()
	}
	if err != nil {
		return out, errors.New("WechatLogin request err :" + err.Error())
	}
	info, _ := ioutil.ReadAll(resp.Body)
	cfg.LogInfo("====info:", string(info))
	json.Unmarshal(info, &out)
	return out, nil
}

var errorCode = map[string]int{
	"IllegalAppID":      -41000,
	"IllegalAesKey":     -41001,
	"IllegalIV":         -41002,
	"IllegalBuffer":     -41003,
	"DecodeBase64Error": -41004,
	"DecodeJsonError":   -41005,
}

// WxBizDataCrypt represents an active WxBizDataCrypt object
type WxBizDataCrypt struct {
	AppID      string
	SessionKey string
}

type showError struct {
	errorCode int
	errorMsg  error
}

func (e showError) Error() string {
	return fmt.Sprintf("{code: %v, error: \"%v\"}", e.errorCode, e.errorMsg)
}

// Decrypt Weixin APP's AES Data
// If isJSON is true, Decrypt return JSON type.
// If isJSON is false, Decrypt return map type.
func (wxCrypt *WxBizDataCrypt) Decrypt(encryptedData string, iv string) ([]byte, error) {
	if len(wxCrypt.SessionKey) != 24 {
		return nil, showError{errorCode["IllegalAesKey"], errors.New("sessionKey length is error")}
	}
	aesKey, err := base64.StdEncoding.DecodeString(wxCrypt.SessionKey)
	if err != nil {
		return nil, showError{errorCode["DecodeBase64Error"], err}
	}

	if len(iv) != 24 {
		return nil, showError{errorCode["IllegalIV"], errors.New("iv length is error")}
	}
	aesIV, err := base64.StdEncoding.DecodeString(iv)
	if err != nil {
		return nil, showError{errorCode["DecodeBase64Error"], err}
	}

	aesCipherText, err := base64.StdEncoding.DecodeString(encryptedData)
	if err != nil {
		return nil, showError{errorCode["DecodeBase64Error"], err}
	}
	aesPlantText := make([]byte, len(aesCipherText))

	aesBlock, err := aes.NewCipher(aesKey)
	if err != nil {
		return nil, showError{errorCode["IllegalBuffer"], err}
	}

	mode := cp.NewCBCDecrypter(aesBlock, aesIV)
	mode.CryptBlocks(aesPlantText, aesCipherText)
	aesPlantText = PKCS7UnPadding(aesPlantText)

	cfg.LogInfo("aesPlantText:", string(aesPlantText))

	re := regexp.MustCompile(`[^\{]*(\{.*\})[^\}]*`)
	aesPlantText = []byte(re.ReplaceAllString(string(aesPlantText), "$1"))

	return aesPlantText, nil
}

// PKCS7UnPadding return unpadding []Byte plantText
func PKCS7UnPadding(plantText []byte) []byte {
	length := len(plantText)
	if length > 0 {
		unPadding := int(plantText[length-1])
		return plantText[:(length - unPadding)]
	}
	return plantText
}
